/**
 * Copyright (C) 2010 Cloudfarming <info@cloudfarming.nl>
 *
 * Licensed under the Eclipse Public License - v 1.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.eclipse.org/legal/epl-v10.html
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package nl.cloudfarming.util.filecopier;

import nl.cloudfarming.util.filecopier.Exception.NotAFileException;
import nl.cloudfarming.util.filecopier.Exception.NotADirectoryException;
import nl.cloudfarming.util.filecopier.Exception.FileRenameException;
import nl.cloudfarming.util.filecopier.Exception.FileMoveException;
import nl.cloudfarming.util.filecopier.Exception.DirectoryCreationException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.security.NoSuchAlgorithmException;
import nl.cloudfarming.sensor.model.impl.SensorServiceImpl;
import nl.cloudfarming.sensor.util.MD5Sum;

/**
 *
 * @author Merijn Zengers, Gerben Feenstra
 */
public class GenericFileCopier {


    public File copyDirectory(File source, File destination ) throws FileNotFoundException, NoSuchAlgorithmException,
                                                                                    IOException, NotADirectoryException, NotAFileException, DirectoryCreationException{
        // TODO: Should destination not be:
        // newDestination = destination.getPath() + source.substring(source.lastIndexOf(File.seperator));
        // Not just copy the files from the source-location, but also the selected folder.
        if(source.isDirectory()){
            for(File f : source.listFiles()){
                if(!fileExists(f) && f.isFile()){
                    copyFile(f, destination);
                }
            }
            return new File(destination.getPath());
        } else{
            StringBuilder sb = new StringBuilder("The suplied path: ");
            sb.append(source.getPath());
            sb.append(". Is not a directory");
            throw new NotADirectoryException(sb.toString());
        }
    }

    /*
     * Copies file at path 'path' to directory 'ToDirectory'.
     */
    public File copyFile(File source, File destination) throws NotADirectoryException, NotAFileException, DirectoryCreationException, FileNotFoundException, IOException, NoSuchAlgorithmException{
        File newFile;
        FileChannel fileChannelSrc = null;
        FileChannel fileChannelDest = null;
        if(source.isFile()){
                if(!destination.exists()){
                    if(!destination.mkdir()){
                        StringBuilder sb = new StringBuilder("Fault while creating directory: ");
                        sb.append(destination.getPath());
                        throw new DirectoryCreationException(sb.toString());
                    }
                }
                StringBuilder sb = new StringBuilder(destination.getPath());
                sb.append(File.separator);
                sb.append(source.getName());
                newFile = new File(sb.toString());

                fileChannelSrc = new FileInputStream(source).getChannel();
                fileChannelDest = new FileOutputStream(newFile).getChannel();
                fileChannelDest.transferFrom(fileChannelSrc, 0, fileChannelSrc.size());
                fileChannelDest.close();
                fileChannelSrc.close();
                return newFile;
        } else{
            StringBuilder sb = new StringBuilder("The suplied path: ");
            sb.append(source.getPath());
            sb.append(". Is not a File");
            throw new NotAFileException(sb.toString());
        }
    }

    /*
     * Method to move a file.
     * The File wil be renamed to the desired name. It wil rside in the same directory the original file was in.
     * @param String originalPath: the path the file curently has
     * @Param Sting toDirectory: the desired directory to which the file must be moved
     * @param indicates if the method is permitted to overwrite any existing files with the same name in the proces of moving.
     */
    public void moveFile(File source, File destination, boolean overwrite) throws NotADirectoryException, NotAFileException, DirectoryCreationException, FileMoveException{
        if(source.isFile()){
            try {
                if (!destination.exists()) {
                    if (!destination.mkdir()) {
                        StringBuilder sb = new StringBuilder("Fault while creating directory: ");
                        sb.append(destination.getPath());
                        throw new DirectoryCreationException(sb.toString());
                    }
                }
                StringBuilder sb = new StringBuilder(destination.getPath());
                sb.append(File.separator);
                sb.append(source.getName());
                renameFile(source, new File(sb.toString()), overwrite);
            } catch (FileRenameException ex) {
                StringBuilder sb2 = new StringBuilder("Unable to move file: ");
                sb2.append(source.getPath());
                throw new FileMoveException(sb2.toString(), ex);
            }
        } else{
            StringBuilder sb = new StringBuilder("The suplied path: ");
            sb.append(source.getPath());
            sb.append(". Is not a File");
            throw new NotAFileException(sb.toString());
        }
    }

    /*
     * Method to rename a file.
     * The File wil be renamed to the desired name. It wil rside in the same directory the original file was in.
     * @param String originalPath: the path the file curently has
     * @Param Sting desiredPath: the desired path for the file
     * @param indicates if the method is permitted to overwrite any existing files with the same name in the proces of renaming.
     * @return the path to the renamed file
     */
    public void renameFile(File source, File destination, boolean overwrite) throws  NotAFileException, FileRenameException{
        if(source.isFile()){
            if(destination.exists()){
                if(overwrite){
                    destination.delete();
                }
            }
            //Abble to rename?
            if(!source.renameTo(destination)){
                StringBuilder sb2 = new StringBuilder("Unable to move file: ");
                sb2.append(source.getPath());
                throw new FileRenameException(sb2.toString());
            }
        } else{
            StringBuilder sb = new StringBuilder("The suplied file: ");
            sb.append(source.getPath());
            sb.append(". Is not a File");
            throw new NotAFileException(sb.toString());
        }
    }

    public boolean fileExists(File newFile) throws FileNotFoundException, NoSuchAlgorithmException, IOException{
        String checkSum = MD5Sum.getHex(MD5Sum.createChecksum(new FileInputStream(newFile)));
        if(new SensorServiceImpl().findGeoSensorDataFileByNameChecksum(newFile.getName(), checkSum) != null){
            return true;
        }
        return false;
    }

}
